package com.kizuki.io;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

public class NoBlockServer {

    static final int PORT = 1248;

    private Selector selector;
    private ServerSocketChannel serverSocketChannel;

    /**
     * SelectionKey.OP_ACCEPT —— 接收连接进行事件，表示服务器监听到了客户连接，那么服务器可以接收这个连接了
     * SelectionKey.OP_CONNECT —— 连接就绪事件，表示客户与服务器的连接已经建立成功
     * SelectionKey.OP_READ  —— 读就绪事件，表示通道中已经有了可读的数据，可以执行读操作了（通道目前有数据，可以进行读操作了）
     * SelectionKey.OP_WRITE —— 写就绪事件，表示已经可以向通道写数据了（通道目前可以用于写操作）
     */
    private void initServer(int port) throws IOException {
        serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.socket().bind(new InetSocketAddress("localhost", port));

        this.selector = Selector.open();
        /**
         * serverSocketChannel注册在selector, 监听连接进行事件
         */
        SelectionKey key = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("serverSocketChannel key = " + key);
    }

    private void listen() throws IOException {
        System.out.println("server started succeed!");

        while (true) {
            /**
             * int select()：阻塞到至少有一个通道在你注册的事件上就绪了。
             * int select(long timeout)：和select()一样，但最长阻塞时间为timeout毫秒。
             * int selectNow()：非阻塞，只要有通道就绪就立刻返回。
             */
            if (selector.select() <= 0) {
                continue;
            }
            Iterator<SelectionKey> ite = selector.selectedKeys().iterator();
            while (ite.hasNext()) {
                SelectionKey key = ite.next();
                // 对应上面的几个事件
                if (key.isAcceptable()) {
                    SocketChannel channel = serverSocketChannel.accept();
                    channel.configureBlocking(false);
                    /**
                     * 将客户端channel注册到selector, 监听读就绪事件
                     */
                    channel.register(selector, SelectionKey.OP_READ);
                }
                else if (key.isReadable()) {
                    recvAndReply(key);
                }
                else if (key.isWritable()) {

                }
                else if (key.isConnectable()) {

                }
                ite.remove();
            }
        }
    }

    private void recvAndReply(SelectionKey key) throws IOException {
        SocketChannel channel = (SocketChannel) key.channel();
        ByteBuffer buffer = ByteBuffer.allocate(256);
        if (channel.read(buffer) != -1) {
            String msg = new String(buffer.array()).trim();
            String replyMsg = "I am a server";
            System.out.println("NIO server received message =  " + msg);
            System.out.println("NIO server reply =  " + replyMsg);
            channel.write(ByteBuffer.wrap(replyMsg.getBytes()));
        }
        else {
            channel.close();
        }
    }

    public static void main(String[] args) throws IOException {
        NoBlockServer server = new NoBlockServer();
        server.initServer(NoBlockServer.PORT);
        server.listen();
    }


}
